package cn.com.hbhgkj.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import cn.com.hbhgkj.common.DaoException;
import cn.com.hbhgkj.common.Pagination;
import cn.com.hbhgkj.dao.GenericDao;

@SuppressWarnings("unchecked")
public class GenericDaoImpl<T extends Serializable, PK extends Serializable>
		extends HibernateDaoSupport implements GenericDao<T, PK> {

	public T get(PK id) throws DaoException {
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	public void update(T entity) throws DaoException {
		try {
			getHibernateTemplate().update(entity);
		} catch (DataAccessException e) {
			LOG.error("Update entity failed!", e.getCause());
			throw new DaoException("Update entity failed!", e.getCause());
		}
	}

	public List<T> getAll(Pagination pagination) throws DaoException {
		try {
			DetachedCriteria criteria = this.createCriteria();
			pagination.setTotalCount(this.getTotalCount(criteria));

			if (pagination.getOrderByField() != null) {
				if (pagination.isAsc())
					criteria.addOrder(Order.asc(pagination.getOrderByField()));
				else
					criteria.addOrder(Order.desc(pagination.getOrderByField()));
			}

			return this.getHibernateTemplate().findByCriteria(criteria,
					pagination.getStart(), pagination.getPageSize());
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	public void save(T entity) throws DaoException {
		try {
			getHibernateTemplate().save(entity);
		} catch (DataAccessException e) {
			LOG.error("Save entity failed!", e.getCause());
			throw new DaoException("Add entity failed!", e.getCause());
		}
	}

	public void saveOrUpdate(T entity) throws DaoException {
		try {
			getHibernateTemplate().saveOrUpdate(entity);
		} catch (DataAccessException e) {
			LOG.error("Save or update entity failed!", e.getCause());
			throw new DaoException("Save or update entity failed!",
					e.getCause());
		}
	}

	public void saveOrUpdateAll(Collection<T> entities) throws DaoException {
		try {
			getHibernateTemplate().saveOrUpdateAll(entities);
		} catch (DataAccessException e) {
			LOG.error("Save or update all entities failed!", e.getCause());
			throw new DaoException("Save or update all entities failed!",
					e.getCause());
		}
	}

	public void delete(T entity) throws DaoException {
		try {
			getHibernateTemplate().delete(entity);
		} catch (DataAccessException e) {
			LOG.error("Delete entity failed!", e.getCause());
			throw new DaoException("Delete entity failed!", e.getCause());
		}
	}

	public void deleteAll(Collection<T> entities) throws DaoException {
		try {
			getHibernateTemplate().deleteAll(entities);
		} catch (DataAccessException e) {
			LOG.error("Delete all entities failed!", e.getCause());
			throw new DaoException("Delete all entities failed!", e.getCause());
		}
	}

	public void closeIterator(Iterator it) {
		getHibernateTemplate().closeIterator(it);
	}

	public DetachedCriteria createCriteria() {
		return DetachedCriteria.forClass(this.entityClass);
	}

	public int getTotalCount(DetachedCriteria criteria) throws DaoException {
		criteria.setProjection(Projections.rowCount());
		List list = this.getHibernateTemplate().findByCriteria(criteria, 0, 1);
		criteria.setProjection(null);
		return (Integer) list.get(0);
	}

	private Class<T> entityClass;
	private static final Log LOG = LogFactory.getLog(GenericDaoImpl.class);

	public GenericDaoImpl() {
		this.entityClass = null;
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.entityClass = (Class<T>) p[0];
		}
	}

}
